module('lgx.decl', package.seeall)
local strbuf = require'lgx.strbuf'

CLASS    = 'Class'
BASE     = 'Base'
STRUCT   = 'Struct'
FUNC     = 'Function'
VAR      = 'Variable'
FARG     = 'Argument'
ELLIPSIS = 'Ellipsis'
METHOD   = 'Method'
FIELD    = 'Field'
TDEF     = 'Typedef'
ENUMVAL  = 'EnumValue'
ENUM     = 'Enumeration'
UNION    = 'Union'
CTOR     = 'Constructor'
DTOR     = 'Destructor'
NS       = 'Namespace'
OP       = 'OperatorMethod'
OPFUNC   = 'OperatorFunction'
ARRAY    = 'ArrayType'
CONV     = 'Converter'
OFFSET   = 'OffsetType'
FUNCTYPE = 'FunctionType'
MTYPE    = 'MethodType'
ATYPE    = 'ArrayType'
FILE     = 'File'
CVTYPE   = 'CvQualifiedType'
RTYPE    = 'ReferenceType'
FTYPE    = 'FundamentalType'
PTYPE    = 'PointerType'
ROOT     = 'GCC_XML'

mt = {}
mt.__index = mt

function new(o)
    o = o or {}
    o.ignore = false
    setmetatable(o, mt)
    return o
end

function mt:exclude()
    self.ignore = true
end

function mt:include()
    self.ignore = false
end

function mt:declfile()
    return self.ids:declfile(self.file)
end

local class    = require'lgx.decls.class'
local base     = require'lgx.decls.base'
local struct   = require'lgx.decls.struct'
local func     = require'lgx.decls.func'
local var      = require'lgx.decls.var'
local farg     = require'lgx.decls.farg'
local ellipsis = require'lgx.decls.ellipsis'
local method   = require'lgx.decls.method'
local field    = require'lgx.decls.field'
local tdef     = require'lgx.decls.tdef'
local enumval  = require'lgx.decls.enumval'
local enum     = require'lgx.decls.enum'
local union    = require'lgx.decls.union'
local ctor     = require'lgx.decls.ctor'
local dtor     = require'lgx.decls.dtor'
local ns       = require'lgx.decls.ns'
local op       = require'lgx.decls.op'
local opfunc   = require'lgx.decls.opfunc'
local array    = require'lgx.decls.array'
local conv     = require'lgx.decls.conv'
local offset   = require'lgx.decls.offset'
local functype = require'lgx.decls.functype'
local mtype    = require'lgx.decls.mtype'
local atype    = require'lgx.decls.atype'
local file     = require'lgx.decls.file'
local cvtype   = require'lgx.decls.cvtype'
local rtype    = require'lgx.decls.rtype'
local ftype    = require'lgx.decls.ftype'
local ptype    = require'lgx.decls.ptype'
local root     = require'lgx.decls.root'

local tagmap = {
    Class            = class.new,
    Base             = base.new,
    Struct           = struct.new,
    Function         = func.new,
    Variable         = var.new,
    Argument         = farg.new,
    Ellipsis         = ellipsis.new,
    Method           = method.new,
    Field            = field.new,
    Typedef          = tdef.new,
    EnumValue        = enumval.new,
    Enumeration      = enum.new,
    Union            = union.new,
    Constructor      = ctor.new,
    Destructor       = dtor.new,
    Namespace        = ns.new,
    OperatorMethod   = op.new,
    OperatorFunction = opfunc.new,
    ArrayType        = array.new,
    Converter        = conv.new,
    OffsetType       = offset.new,
    FunctionType     = functype.new,
    MethodType       = mtype.new,
    ArrayType        = atype.new,
    File             = file.new,
    CvQualifiedType  = cvtype.new,
    ReferenceType    = rtype.new,
    FundamentalType  = ftype.new,
    PointerType      = ptype.new,
    GCC_XML          = root.new,
}


function newdecl(tag, attr)
	local ctor = tagmap[tag]
	if ctor then
        for i=1,#attr do
            table.remove(attr)
        end
		return ctor(attr)
	else
		return nil
	end
end

function mt:addtag(tag, attr)
    local d = newdecl(tag, attr)
    if d then
        self.addsub(tag, d)
    else
        assert(false, string.format('lgx:tag "%s" is invalid', tag))
    end
end

function mt:addsub(tag, sub)
    assert(false,string.format('lgx:decl(%s)\'s addsub(%s) should not be called!', tag,sub.tag))
end

function mt:isclass()
    return self.tag == CLASS or self.tag == STRUCT or self.tag == UNION
end

function mt:ismember()
    return self.tag == METHOD or self.tag == OPFUNC or self.tag == FIELD 
        or self.tag == CTOR or self.tag == DTOR
end

function mt:isfunction()
    return self.tag == METHOD or self.tag == OPFUNC or self.tag == FUNC
        or self.tag == CTOR or self.tag == DTOR
end

------------------------------------------------------------------------
--  give a attr name whose value is like '_1 _2 _3...' and 
--  return an array which include decl whose 'id' is '_1' '_2' ...
------------------------------------------------------------------------
function mt:idlist(attrname)
    return self.ids:list(self[attrname])
end

function mt:ctx()
    if self.baselist and #self.baselist > 0 then return nil end
    return self.ids[self.context] or nil
end

------------------------------------------------------------------------
--  get full ns name by type id
------------------------------------------------------------------------
function mt:typename(id)
    local id = id or self.id
    return self.ids:typename(id)
end

function mt:typeinfo(id)
    local id = id or self.id
    return self.ids:typeinfo(id)
end

function mt:typetag(id)
    local id = id or self.id
    return self.ids[id].tag
end

------------------------------------------------------------------------
--  get full ns name of self  
------------------------------------------------------------------------
function mt:nsname(prefix, suffix)
    assert(self.name, string.format('"%s".name is nil', self.tag))
    if self.name == '::' then return '' end
    prefix = prefix or ''
    suffix = suffix or ''
    if not self.context then return prefix..self.name..suffix end
    local ctxnsname = self.ids[self.context]:nsname()
    return prefix..(ctxnsname=='' and '' or ctxnsname..'::')..self.name..suffix
    --return self.ids:nsname(self.id, prefix, suffix)
end

function mt:safename()
    return string.gsub(self.name, '[:|<|>|,| ]+', '_')
end

function mt:name_(prefix, suffix)
    return (prefix or '')..self:safename()..(suffix or '')
end

------------------------------------------------------------------------
--  get full ns name of self, but replace '::' by '_'
------------------------------------------------------------------------
function mt:nsname_(prefix, suffix)
    return string.gsub(self:nsname(), '[<|>|,|:| ]+', '_')
end

function mt:__tostring()
    local s = strbuf.new()
    for k,v in pairs(self) do
        s = s + string.format('[%s]=%s', tostring(k), tostring(v))
    end
    return '{'..s:concat(',')..'}'
end

